import rtmidi
import tkinter
from tkinter import ttk

# Set up MIDI output using python-rtmidi
channel = 0
midi_out = rtmidi.MidiOut()
midi_out.open_port(channel)  # Adjust the port number as needed

# Define the mapping between keys and MIDI notes
qwerty = "qazwsxedcrfvtgbyhnujmik,ol.p;/['"
key_to_note = {char: (index + 12 * (3 - (-1)) + 9)
               for index, char in enumerate(qwerty)}
for i, j in zip("234567890-", "zxcvbnm,./"):
    key_to_note[i] = key_to_note[j]
for i in key_to_note.copy().keys():
    if i.islower():
        key_to_note[i.upper()] = key_to_note[i] - 24
for i, j in zip("234567890-,./;'[", '@#$%^&*()_<>?:"{'):
    key_to_note[j] = key_to_note[i] - 24
note_name = [' C', 'C#', ' D', 'Eb', ' E',
             ' F', 'F#', ' G', 'G#', ' A', 'Bb', ' B']
k2n = {key: note_name[note % 12] +
       str(note // 12 - 1) for key, note in key_to_note.items()}

# 把qwerty拆成三行
names = [[k2n[i] for i in qwerty[j::3]] for j in range(3)]
names_str = '\n'.join('  ' * j + '   '.join(names[j]) for j in range(3))


# Keep track of pressed keys
pressed_keys = set()
sustained_keys = set()

# Keep track of whether the sustain pedal (spacebar) is pressed
sustain_pedal_pressed = False

# Function to send MIDI messages and update the GUI


def send_midi_note(key, action, channel=channel):
    actions = {"press": 0x90, "release": 0x80}
    act_code = actions[action] + channel
    note = key_to_note[key]
    velocity = 64
    midi_out.send_message([act_code, note, velocity])


# Function to handle sustain pedal (spacebar) press and release
def on_sustain_pedal_press(event):
    global sustain_pedal_pressed
    if not sustain_pedal_pressed:
        sustain_pedal_pressed = True


def on_sustain_pedal_release(event):
    global sustain_pedal_pressed
    if sustain_pedal_pressed:
        sustain_pedal_pressed = False
        # Release all keys if sustain pedal is not pressed
        for key in sustained_keys.copy():
            send_midi_note(key, "release")
        sustained_keys.clear()


# Function to update the GUI with pressed notes
def update_gui():
    pressed_notes = [k2n[i] for i in pressed_keys]
    sustained_notes = [k2n[i] for i in sustained_keys]
    notes_label.config(text='\n'.join([
        names_str,
        'Pressed Notes:', ', '.join(pressed_notes),
        'Sustained Notes:', ', '.join(sustained_notes)
    ]))
    root.after(10, update_gui)


# GUI setup
root = tkinter.Tk()
root.title("MIDI Keyboard")

notes_label = ttk.Label(root, text=names_str +
                        "\n Pressed Notes: \n", font="Consolas")
notes_label.pack(pady=10)

# Bind key press and release events


def on_key_press(event):
    key = event.char
    if key == ' ':
        on_sustain_pedal_press(event)
    elif key in key_to_note and key not in pressed_keys:
        send_midi_note(key, "press")
        pressed_keys.add(key)
        if key in sustained_keys:
            sustained_keys.remove(key)


def on_key_release(event):
    key = event.char
    if key == ' ':
        on_sustain_pedal_release(event)
    if key in key_to_note and key in pressed_keys:
        if sustain_pedal_pressed:
            sustained_keys.add(key)
        else:
            send_midi_note(key, "release")
        pressed_keys.remove(key)


root.bind("<KeyPress>", on_key_press)
root.bind("<KeyRelease>", on_key_release)

# Start the GUI update loop
update_gui()

# Run the GUI
root.mainloop()

# Cleanup
# No need to close the midi_out object for python-rtmidi
# midi_out.close()
